#pragma once

#include "base.h"
#include "corebase_global.h"
#include <osg/Vec3>
#include <ode/common.h>
#include <ode/collision_space.h>
#include <osg/GL>
#include <QVector>
#include <osg/Group>
#include <osg/LightSource>
#include <osg\LightModel>

namespace osg
{
	class Light;
	class Fog;
}

namespace osgParticle
{
	class ParticleSystem;
}

namespace CoreBase
{
	class Transformable;
	class ODEController;
	class DatabasePager;
	class LightSource;
	class Viewer;
	class Drawable;
	class SceneImpl;

	/**
	*  Scene: This class encapsulates the root of the delta scene graph
	*/
	class COREBASE_EXPORT Scene : public CoreBase::Base
	{
	public:
		enum Mode
		{
			POINT = GL_POINT,
			LINE = GL_LINE,
			FILL = GL_FILL
		};
		enum Face
		{
			FRONT,
			BACK,
			FRONT_AND_BACK
		};
		Scene(const QString& name = "scene");
		Scene(ODEController* physicsController, const QString& name = "scene");
	protected:
		virtual ~Scene();
	public:
		osg::Group* getSceneNode();
		void setSceneNode(osg::Group* newscenenode);
		void addDrawable(Drawable* drawable);
		void removeDrawable(Drawable* drawable);
		void removeAllDrawables();
		Drawable* getDrawable(unsigned int i) const;
		unsigned int getDrawableIndex(const Drawable* drawable) const;
		unsigned int getNumberOfAddedDrawable() const;
		QVector<Drawable*> getAllDrawablesInTheScene() const;
		void setRenderState(Face face, Mode mode);
		const std::pair<Face,Mode> getRenderState() const;
		float getHeightOfTerrain(float x, float y);
		bool getHeightOfTerrain(float& heightofterrain, float x, float y, float maxZ = 10000.f, float minZ = -10000);
		///get the ODE space ID
		dSpaceID getSpaceID() const;
		///get the ODE world ID
		dWorldID getWorldID() const;
		///get the ODE contact joint group ID
		dJointGroupID getContactJointGroupID() const;
		//set the gravity vector
		void setGravity(const osg::Vec3& gravity) const;
		///set the gravity vector
		void setGravity(float x, float y, float z) const 
		{
			setGravity(osg::Vec3(x, y, z));
		}
		///get the gravity vector
		void getGravity(osg::Vec3& vec) const;
		///get the gravity vector
		osg::Vec3 getGravity() const;
		///get the gravity vector
		void getGravity(float& x, float& y, float& z) const;
		//Performs collision detection and updates physics
		//The user data associated with "collision" messages
		struct COREBASE_EXPORT CollisionData
		{
		   Transformable* mBodies[2]; ///<The bodies colliding
		   osg::Vec3 mLocation; ///<The collision location
		   osg::Vec3 mNormal; ///<The collision normal
		   float mDepth; ///<The penetration depth
		};
		///Supply a user-defined collision callback to replace the internal one
		void setUserCollisionCallback(dNearCallback* func, void* data=NULL) const;
		dNearCallback* getUserCollisionCallback() const;
		void* getUserCollisionData();
		const void* getUserCollisionData() const;
		/**
		 * get the step size of the physics.  The physics will
		 * be updated numerous times per frame based on this number.  For example,
		 * if the delta frame rate is 33ms and the step size is 2ms, the physics
		 * will be updated 16 times.
		 *
		 * @return the step size in seconds
		 * @see setPhysicsStepSize()
		 */
		double getPhysicsStepSize() const;
		/// @see getPhysicsStepSize()
		void setPhysicsStepSize(double stepSize = 0.0) const;
		/// Register a Transformable with the Scene
		void registerCollidable(Transformable* collidable) const;
		/// UnRegister a Transformable with the Scene
		void unRegisterCollidable(Transformable* collidable) const;
		/// get handle to the internal physics controller (could be NULL)
		ODEController* getPhysicsController() const;
		void enableOde(bool enable);
		osg::Light* getLight(const QString& name);
		const osg::Light* getLight(const QString& name) const;
		void registerLight(const QString& key,osg::LightSource* light);
		void unRegisterLight(const QString& key);
		void useSceneLight(const QString& key,bool lightState = true);
		void setSceneLightModel(osg::ref_ptr<osg::LightModel> model);
		void getDrawableChildren(QVector<Drawable*>& children,Drawable& parent) const;
		/**
		* This function clears out the database pager and then re-registers the remaining LOD's.
		*  A good time to do this is on map load.
		*/
		//void ResetDatabasePager();
		void removeView(Viewer& view);
		void addView(Viewer& view);
		virtual void frameStepUpdate(double);
		virtual void postEventTraversal(double);
		virtual void prePhysicsStepUpdate(double);
		virtual void postPhysicsStepUpdate(double);
	public slots:
		virtual void onMessage(MessageData* data);
	protected:
		friend class Viewer;
		void updateViewSet();
		/// Exists for unit tests.
		bool isAssignedToView(Viewer& view) const;
		/// define the owner mView of this instance
		//void RemoveView(dtCore::Viewer& view);
		//void AddView(dtCore::Viewer& view);
		//void SetDatabasePager(dtCore::DatabasePager *pager);
		/// Made public so I can be unit tested better.
		//dtCore::DatabasePager* getDatabasePager() const;
	private:
		// Disallowed to prevent compile errors on VS2003. It apparently
		// creates this functions even if they are not used, and if
		// this class is forward declared, these implicit functions will
		// cause compiler errors for missing calls to "ref".
		bool									_fogOn;
		bool									_updateOde;
		void ctor();
		//void getDrawableChildren(QVector<dtCore::DeltaDrawable*>& children,
		//	dtCore::DeltaDrawable& parent) const;
		//void UpdateViewSet();
		SceneImpl*								_impl;
	};
}